/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.google.code.twiddling.core.chain;

import com.google.code.twiddling.core.command.CommandContext;
import com.google.code.twiddling.core.chain.ChainException;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Arrays;

/**
 * @author <a href="mailto:yy.young@gmail.com">Young Yang</a>
 * @create Dec 8, 2008 4:23:06 PM
 */
public class ChainManager {

    private static List<Processor> executorChain = new ArrayList<Processor>();

    private static ThreadLocal<Iterator<Processor>> tlchain = new ThreadLocal<Iterator<Processor>>(){
        @Override
        protected Iterator<Processor> initialValue() {
            return new ArrayList<Processor>(executorChain).iterator();
        }
    };

    private final static ChainManager instance = new ChainManager();


    private ChainManager() {
        try {
            this.addProcessor(HistoryProcessor.class);
            this.addProcessor(CommandLineParserProcessor.class);
            this.addProcessor(CommandProcessor.class);
        }
        catch (Exception e) {
            throw new RuntimeException("Failed to initialize ChainManager.", e);
        }

    }


    public static ChainManager getInstance(){
        return instance;
    }

    private static Iterator<Processor> getCurrentProcessChain(){
        return tlchain.get();
    }

    public void addProcessor(Class<? extends Processor> clz) throws Exception {
        executorChain.add(clz.newInstance());
    }

    public void processChain(final CommandContext commandContext) throws Exception{
        try {
            next(commandContext);
        }
        finally {
            tlchain.remove();
        }
    }

    void next(CommandContext commandContext) throws Exception{
        Iterator<Processor> currentChain = getCurrentProcessChain();
        if(currentChain.hasNext()) {
            currentChain.next().next(commandContext);
        }
    }

    public String info(){
        return this.getClass().getSimpleName() + " info: " + Arrays.toString(executorChain.toArray());
    }

}
